/*
 *  This file is called main.c, because it contains most of the new functions
 *  for use with LibVNCServer.
 *
 *  LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de>
 *  Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
 *  Original Xvnc (C) 1999 AT&T Laboratories Cambridge.  
 *  All Rights Reserved.
 *
 *  see GPL (latest version) for full details
 */

#ifdef __STRICT_ANSI__
#define _BSD_SOURCE
#endif
#include <rfb/rfb.h>
#include <rfb/rfbregion.h>
#include "private.h"

#include <stdarg.h>
#include <errno.h>

#ifndef false
#define false 0
#define true -1
#endif

#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#endif

#include <signal.h>
#include <time.h>

static int extMutex_initialized = 0;
static int logMutex_initialized = 0;
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
static MUTEX(logMutex);
static MUTEX(extMutex);
#endif

static int rfbEnableLogging=1;

#ifdef LIBVNCSERVER_WORDS_BIGENDIAN
char rfbEndianTest = (1==0);
#else
char rfbEndianTest = (1==1);
#endif

/*
 * Protocol extensions
 */

static rfbProtocolExtension* rfbExtensionHead = NULL;

/*
 * This method registers a list of new extensions.  
 * It avoids same extension getting registered multiple times. 
 * The order is not preserved if multiple extensions are
 * registered at one-go.
 */
void
rfbRegisterProtocolExtension(rfbProtocolExtension* extension)
{
	rfbProtocolExtension *head = rfbExtensionHead, *next = NULL;

	if(extension == NULL)
		return;

	next = extension->next;

	if (! extMutex_initialized) {
		INIT_MUTEX(extMutex);
		extMutex_initialized = 1;
	}

	LOCK(extMutex);

	while(head != NULL) {
		if(head == extension) {
			UNLOCK(extMutex);
			rfbRegisterProtocolExtension(next);
			return;
		}

		head = head->next;
	}

	extension->next = rfbExtensionHead;
	rfbExtensionHead = extension;

	UNLOCK(extMutex);
	rfbRegisterProtocolExtension(next);
}

/*
 * This method unregisters a list of extensions.  
 * These extensions won't be available for any new
 * client connection. 
 */
void
rfbUnregisterProtocolExtension(rfbProtocolExtension* extension)
{

	rfbProtocolExtension *cur = NULL, *pre = NULL;

	if(extension == NULL)
		return;

	if (! extMutex_initialized) {
		INIT_MUTEX(extMutex);
		extMutex_initialized = 1;
	}

	LOCK(extMutex);

	if(rfbExtensionHead == extension) {
		rfbExtensionHead = rfbExtensionHead->next;
		UNLOCK(extMutex);
		rfbUnregisterProtocolExtension(extension->next);
		return;
	}

	cur = pre = rfbExtensionHead;

	while(cur) {
		if(cur == extension) {
			pre->next = cur->next;
			break;
		}
		pre = cur;
		cur = cur->next;
	}

	UNLOCK(extMutex);

	rfbUnregisterProtocolExtension(extension->next);
}

rfbProtocolExtension* rfbGetExtensionIterator()
		{
	LOCK(extMutex);
	return rfbExtensionHead;
		}

void rfbReleaseExtensionIterator()
{
	UNLOCK(extMutex);
}

rfbBool rfbEnableExtension(rfbClientPtr cl, rfbProtocolExtension* extension,
		void* data)
{
	rfbExtensionData* extData;

	/* make sure extension is not yet enabled. */
	for(extData = cl->extensions; extData; extData = extData->next)
		if(extData->extension == extension)
			return FALSE;

	extData = calloc(sizeof(rfbExtensionData),1);
	extData->extension = extension;
	extData->data = data;
	extData->next = cl->extensions;
	cl->extensions = extData;

	return TRUE;
}

rfbBool rfbDisableExtension(rfbClientPtr cl, rfbProtocolExtension* extension)
{
	rfbExtensionData* extData;
	rfbExtensionData* prevData = NULL;

	for(extData = cl->extensions; extData; extData = extData->next) {
		if(extData->extension == extension) {
			if(extData->data)
				free(extData->data);
			if(prevData == NULL)
				cl->extensions = extData->next;
			else
				prevData->next = extData->next;
			return TRUE;
		}
		prevData = extData;
	}

	return FALSE;
}

void* rfbGetExtensionClientData(rfbClientPtr cl, rfbProtocolExtension* extension)
		{
	rfbExtensionData* data = cl->extensions;

	while(data && data->extension != extension)
		data = data->next;

	if(data == NULL) {
		rfbLog("Extension is not enabled !\n");
		/* rfbCloseClient(cl); */
		return NULL;
	}

	return data->data;
		}

/*
 * Logging
 */

void rfbLogEnable(int enabled) {
	rfbEnableLogging=enabled;
}

/*
 * rfbLog prints a time-stamped message to the log file (stderr).
 */

static void
rfbDefaultLog(const char *format, ...)
{
	va_list args;
	char buf[256];
	time_t log_clock;

	if(!rfbEnableLogging)
		return;

	if (! logMutex_initialized) {
		INIT_MUTEX(logMutex);
		logMutex_initialized = 1;
	}

	LOCK(logMutex);
	va_start(args, format);

	time(&log_clock);
	strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock));
	fprintf(stderr,buf);

	vfprintf(stderr, format, args);
	fflush(stderr);

	va_end(args);
	UNLOCK(logMutex);
}

rfbLogProc rfbLog=rfbDefaultLog;
rfbLogProc rfbErr=rfbDefaultLog;

void rfbLogPerror(const char *str)
{
	rfbErr("%s: %s\n", str, strerror(errno));
}

void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)
{  
	rfbClientIteratorPtr iterator;
	rfbClientPtr cl;

	iterator=rfbGetClientIterator(rfbScreen);
	while((cl=rfbClientIteratorNext(iterator))) {
		LOCK(cl->updateMutex);
		if(cl->useCopyRect) {
			sraRegionPtr modifiedRegionBackup;
			if(!sraRgnEmpty(cl->copyRegion)) {
				if(cl->copyDX!=dx || cl->copyDY!=dy) {
					/* if a copyRegion was not yet executed, treat it as a
					 * modifiedRegion. The idea: in this case it could be
					 * source of the new copyRect or modified anyway. */
					sraRgnOr(cl->modifiedRegion,cl->copyRegion);
					sraRgnMakeEmpty(cl->copyRegion);
				} else {
					/* we have to set the intersection of the source of the copy
					 * and the old copy to modified. */
					modifiedRegionBackup=sraRgnCreateRgn(copyRegion);
					sraRgnOffset(modifiedRegionBackup,-dx,-dy);
					sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
					sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
					sraRgnDestroy(modifiedRegionBackup);
				}
			}

			sraRgnOr(cl->copyRegion,copyRegion);
			cl->copyDX = dx;
			cl->copyDY = dy;

			/* if there were modified regions, which are now copied,
			 * mark them as modified, because the source of these can be overlapped
			 * either by new modified or now copied regions. */
			modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion);
			sraRgnOffset(modifiedRegionBackup,dx,dy);
			sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
			sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
			sraRgnDestroy(modifiedRegionBackup);

			if(!cl->enableCursorShapeUpdates) {
				/*
				 * n.b. (dx, dy) is the vector pointing in the direction the
				 * copyrect displacement will take place.  copyRegion is the
				 * destination rectangle (say), not the source rectangle.
				 */
				sraRegionPtr cursorRegion;
				int x = cl->cursorX - cl->screen->cursor->xhot;
				int y = cl->cursorY - cl->screen->cursor->yhot;
				int w = cl->screen->cursor->width;
				int h = cl->screen->cursor->height;

				cursorRegion = sraRgnCreateRect(x, y, x + w, y + h);
				sraRgnAnd(cursorRegion, cl->copyRegion);
				if(!sraRgnEmpty(cursorRegion)) {
					/*
					 * current cursor rect overlaps with the copy region *dest*,
					 * mark it as modified since we won't copy-rect stuff to it.
					 */
					sraRgnOr(cl->modifiedRegion, cursorRegion);
				}
				sraRgnDestroy(cursorRegion);

				cursorRegion = sraRgnCreateRect(x, y, x + w, y + h);
				/* displace it to check for overlap with copy region source: */
				sraRgnOffset(cursorRegion, dx, dy);
				sraRgnAnd(cursorRegion, cl->copyRegion);
				if(!sraRgnEmpty(cursorRegion)) {
					/*
					 * current cursor rect overlaps with the copy region *source*,
					 * mark the *displaced* cursorRegion as modified since we
					 * won't copyrect stuff to it.
					 */
					sraRgnOr(cl->modifiedRegion, cursorRegion);
				}
				sraRgnDestroy(cursorRegion);
			}

		} else {
			sraRgnOr(cl->modifiedRegion,copyRegion);
		}
		TSIGNAL(cl->updateCond);
		UNLOCK(cl->updateMutex);
	}

	rfbReleaseClientIterator(iterator);
}

void rfbDoCopyRegion(rfbScreenInfoPtr screen,sraRegionPtr copyRegion,int dx,int dy)
{
	sraRectangleIterator* i;
	sraRect rect;
	int j,widthInBytes,bpp=screen->serverFormat.bitsPerPixel/8,
			rowstride=screen->paddedWidthInBytes;
	char *in,*out;

	/* copy it, really */
	i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0);
	while(sraRgnIteratorNext(i,&rect)) {
		widthInBytes = (rect.x2-rect.x1)*bpp;
		out = screen->frameBuffer+rect.x1*bpp+rect.y1*rowstride;
		in = screen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride;
		if(dy<0)
			for(j=rect.y1;j<rect.y2;j++,out+=rowstride,in+=rowstride)
				memmove(out,in,widthInBytes);
		else {
			out += rowstride*(rect.y2-rect.y1-1);
			in += rowstride*(rect.y2-rect.y1-1);
			for(j=rect.y2-1;j>=rect.y1;j--,out-=rowstride,in-=rowstride)
				memmove(out,in,widthInBytes);
		}
	}
	sraRgnReleaseIterator(i);

	rfbScheduleCopyRegion(screen,copyRegion,dx,dy);
}

void rfbDoCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)
{
	sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
	rfbDoCopyRegion(screen,region,dx,dy);
	sraRgnDestroy(region);
}

void rfbScheduleCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)
{
	sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
	rfbScheduleCopyRegion(screen,region,dx,dy);
	sraRgnDestroy(region);
}

void rfbMarkRegionAsModified(rfbScreenInfoPtr screen,sraRegionPtr modRegion)
{
	rfbClientIteratorPtr iterator;
	rfbClientPtr cl;

	iterator=rfbGetClientIterator(screen);
	while((cl=rfbClientIteratorNext(iterator))) {
		LOCK(cl->updateMutex);
		sraRgnOr(cl->modifiedRegion,modRegion);
		TSIGNAL(cl->updateCond);
		UNLOCK(cl->updateMutex);
	}

	rfbReleaseClientIterator(iterator);
}

void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2);
void rfbMarkRectAsModified(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2)
{
	sraRegionPtr region;
	int i;

	if(x1>x2) { i=x1; x1=x2; x2=i; }
	if(x1<0) x1=0;
	if(x2>screen->width) x2=screen->width;
	if(x1==x2) return;

	if(y1>y2) { i=y1; y1=y2; y2=i; }
	if(y1<0) y1=0;
	if(y2>screen->height) y2=screen->height;
	if(y1==y2) return;

	/* update scaled copies for this rectangle */
	rfbScaledScreenUpdate(screen,x1,y1,x2,y2);

	region = sraRgnCreateRect(x1,y1,x2,y2);
	rfbMarkRegionAsModified(screen,region);
	sraRgnDestroy(region);
}

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
#include <unistd.h>

static void *
clientOutput(void *data)
{
	rfbClientPtr cl = (rfbClientPtr)data;
	rfbBool haveUpdate;
	sraRegion* updateRegion;

	while (1) {
		haveUpdate = false;
		while (!haveUpdate) {
			if (cl->sock == -1) {
				/* Client has disconnected. */
				return NULL;
			}
			LOCK(cl->updateMutex);
			haveUpdate = FB_UPDATE_PENDING(cl);
			if(!haveUpdate) {
				updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
				haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion);
				sraRgnDestroy(updateRegion);
			}

			if (!haveUpdate) {
				WAIT(cl->updateCond, cl->updateMutex);
			}
			UNLOCK(cl->updateMutex);
		}

		/* OK, now, to save bandwidth, wait a little while for more
           updates to come along. */
		usleep(cl->screen->deferUpdateTime * 1000);

		/* Now, get the region we're going to update, and remove
           it from cl->modifiedRegion _before_ we send the update.
           That way, if anything that overlaps the region we're sending
           is updated, we'll be sure to do another update later. */
		LOCK(cl->updateMutex);
		updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
		UNLOCK(cl->updateMutex);

		/* Now actually send the update. */
		rfbIncrClientRef(cl);
		rfbSendFramebufferUpdate(cl, updateRegion);
		rfbDecrClientRef(cl);

		sraRgnDestroy(updateRegion);
	}

	/* Not reached. */
	return NULL;
}

static void *
clientInput(void *data)
{
	rfbClientPtr cl = (rfbClientPtr)data;
	pthread_t output_thread;
	pthread_create(&output_thread, NULL, clientOutput, (void *)cl);

	while (1) {
		fd_set rfds, wfds, efds;
		struct timeval tv;
		int n;

		FD_ZERO(&rfds);
		FD_SET(cl->sock, &rfds);
		FD_ZERO(&efds);
		FD_SET(cl->sock, &efds);

		/* Are we transferring a file in the background? */
		FD_ZERO(&wfds);
		if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1))
			FD_SET(cl->sock, &wfds);

		tv.tv_sec = 60; /* 1 minute */
		tv.tv_usec = 0;
		n = select(cl->sock + 1, &rfds, &wfds, &efds, &tv);
		if (n < 0) {
			rfbLogPerror("ReadExact: select");
			break;
		}
		if (n == 0) /* timeout */
		{
			rfbSendFileTransferChunk(cl);
			continue;
		}

		/* We have some space on the transmit queue, send some data */
		if (FD_ISSET(cl->sock, &wfds))
			rfbSendFileTransferChunk(cl);

		if (FD_ISSET(cl->sock, &rfds) || FD_ISSET(cl->sock, &efds))
			rfbProcessClientMessage(cl);

		if (cl->sock == -1) {
			/* Client has disconnected. */
			break;
		}
	}

	/* Get rid of the output thread. */
	LOCK(cl->updateMutex);
	TSIGNAL(cl->updateCond);
	UNLOCK(cl->updateMutex);
	IF_PTHREADS(pthread_join(output_thread, NULL));

	rfbClientConnectionGone(cl);

	return NULL;
}

static void*
listenerRun(void *data)
{
	rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data;
	int client_fd;
	struct sockaddr_in peer;
	rfbClientPtr cl;
	socklen_t len;

	len = sizeof(peer);

	/* TODO: this thread wont die by restarting the server */
	/* TODO: HTTP is not handled */
	while ((client_fd = accept(screen->listenSock,
			(struct sockaddr*)&peer, &len)) >= 0) {
		cl = rfbNewClient(screen,client_fd);
		len = sizeof(peer);

		if (cl && !cl->onHold )
			rfbStartOnHoldClient(cl);
	}
	return(NULL);
}

void 
rfbStartOnHoldClient(rfbClientPtr cl)
{
	pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl);
}

#else

void 
rfbStartOnHoldClient(rfbClientPtr cl)
{
	cl->onHold = FALSE;
}

#endif

void 
rfbRefuseOnHoldClient(rfbClientPtr cl)
{
	rfbCloseClient(cl);
	rfbClientConnectionGone(cl);
}

static void
rfbDefaultKbdAddEvent(rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
{
}

void
rfbDefaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl)
{
	rfbClientIteratorPtr iterator;
	rfbClientPtr other_client;
	rfbScreenInfoPtr s = cl->screen;

	if (x != s->cursorX || y != s->cursorY) {
		LOCK(s->cursorMutex);
		s->cursorX = x;
		s->cursorY = y;
		UNLOCK(s->cursorMutex);

		/* The cursor was moved by this client, so don't send CursorPos. */
		if (cl->enableCursorPosUpdates)
			cl->cursorWasMoved = FALSE;

		/* But inform all remaining clients about this cursor movement. */
		iterator = rfbGetClientIterator(s);
		while ((other_client = rfbClientIteratorNext(iterator)) != NULL) {
			if (other_client != cl && other_client->enableCursorPosUpdates) {
				other_client->cursorWasMoved = TRUE;
			}
		}
		rfbReleaseClientIterator(iterator);
	}
}

static void rfbDefaultSetXCutText(char* text, int len, rfbClientPtr cl)
{
}

/* TODO: add a nice VNC or RFB cursor */

#if defined(WIN32) || defined(sparc) || !defined(NO_STRICT_ANSI)
static rfbCursor myCursor = 
{
		FALSE, FALSE, FALSE, FALSE,
		(unsigned char*)"\000\102\044\030\044\102\000",
		(unsigned char*)"\347\347\176\074\176\347\347",
		8, 7, 3, 3,
		0, 0, 0,
		0xffff, 0xffff, 0xffff,
		NULL, NULL,
		FALSE
};
#else
static rfbCursor myCursor = 
{
		cleanup: FALSE,
		cleanupSource: FALSE,
		cleanupMask: FALSE,
		cleanupRichSource: FALSE,
		source: "\000\102\044\030\044\102\000",
		mask:   "\347\347\176\074\176\347\347",
		width: 8, height: 7, xhot: 3, yhot: 3,
		foreRed: 0, foreGreen: 0, foreBlue: 0,
		backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff,
		richSource: NULL
};
#endif

static rfbCursorPtr rfbDefaultGetCursorPtr(rfbClientPtr cl)
{
	return(cl->screen->cursor);
}

/* response is cl->authChallenge vncEncrypted with passwd */
static rfbBool rfbDefaultPasswordCheck(rfbClientPtr cl,const char* response,int len)
{
	int i;
	char *passwd=rfbDecryptPasswdFromFile(cl->screen->authPasswdData);

	if(!passwd) {
		rfbErr("Couldn't read password file: %s\n",cl->screen->authPasswdData);
		return(FALSE);
	}

	rfbEncryptBytes(cl->authChallenge, passwd);

	/* Lose the password from memory */
	for (i = strlen(passwd); i >= 0; i--) {
		passwd[i] = '\0';
	}

	free(passwd);

	if (memcmp(cl->authChallenge, response, len) != 0) {
		rfbErr("authProcessClientMessage: authentication failed from %s\n",
				cl->host);
		return(FALSE);
	}

	return(TRUE);
}

/* for this method, authPasswdData is really a pointer to an array
   of char*'s, where the last pointer is 0. */
rfbBool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len)
{
	char **passwds;
	int i=0;

	for(passwds=(char**)cl->screen->authPasswdData;*passwds;passwds++,i++) {
		uint8_t auth_tmp[CHALLENGESIZE];
		memcpy((char *)auth_tmp, (char *)cl->authChallenge, CHALLENGESIZE);
		rfbEncryptBytes(auth_tmp, *passwds);

		if (memcmp(auth_tmp, response, len) == 0) {
			if(i>=cl->screen->authPasswdFirstViewOnly)
				cl->viewOnly=TRUE;
			return(TRUE);
		}
	}

	rfbErr("authProcessClientMessage: authentication failed from %s\n",
			cl->host);
	return(FALSE);
}

void rfbDoNothingWithClient(rfbClientPtr cl)
{
}

static enum rfbNewClientAction rfbDefaultNewClientHook(rfbClientPtr cl)
{
	return RFB_CLIENT_ACCEPT;
}

/*
 * Update server's pixel format in screenInfo structure. This
 * function is called from rfbGetScreen() and rfbNewFramebuffer().
 */

static void rfbInitServerFormat(rfbScreenInfoPtr screen, int bitsPerSample)
{
	rfbPixelFormat* format=&screen->serverFormat;

	format->bitsPerPixel = screen->bitsPerPixel;
	format->depth = screen->depth;
	format->bigEndian = rfbEndianTest?FALSE:TRUE;
	format->trueColour = TRUE;
	screen->colourMap.count = 0;
	screen->colourMap.is16 = 0;
	screen->colourMap.data.bytes = NULL;

	if (format->bitsPerPixel == 8) {
		format->redMax = 7;
		format->greenMax = 7;
		format->blueMax = 3;
		format->redShift = 0;
		format->greenShift = 3;
		format->blueShift = 6;
	} else {
		format->redMax = (1 << bitsPerSample) - 1;
		format->greenMax = (1 << bitsPerSample) - 1;
		format->blueMax = (1 << bitsPerSample) - 1;
		if(rfbEndianTest) {
			format->redShift = 0;
			format->greenShift = bitsPerSample;
			format->blueShift = bitsPerSample * 2;
		} else {
			if(format->bitsPerPixel==8*3) {
				format->redShift = bitsPerSample*2;
				format->greenShift = bitsPerSample*1;
				format->blueShift = 0;
			} else {
				format->redShift = bitsPerSample*3;
				format->greenShift = bitsPerSample*2;
				format->blueShift = bitsPerSample;
			}
		}
	}
}

rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
		int width,int height,int bitsPerSample,int samplesPerPixel,
		int bytesPerPixel)
{
	rfbScreenInfoPtr screen=calloc(sizeof(rfbScreenInfo),1);

	if (! logMutex_initialized) {
		INIT_MUTEX(logMutex);
		logMutex_initialized = 1;
	}


	if(width&3)
		rfbErr("WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width);

	screen->autoPort=FALSE;
	screen->clientHead=NULL;
	screen->pointerClient=NULL;
	screen->port=5900;
	screen->socketState=RFB_SOCKET_INIT;

	screen->inetdInitDone = FALSE;
	screen->inetdSock=-1;

	screen->udpSock=-1;
	screen->udpSockConnected=FALSE;
	screen->udpPort=0;
	screen->udpClient=NULL;

	screen->maxFd=0;
	screen->listenSock=-1;

	screen->httpInitDone=FALSE;
	screen->httpEnableProxyConnect=FALSE;
	screen->httpPort=0;
	screen->httpDir=NULL;
	screen->httpListenSock=-1;
	screen->httpSock=-1;

	screen->desktopName = "LibVNCServer";
	screen->alwaysShared = FALSE;
	screen->neverShared = FALSE;
	screen->dontDisconnect = FALSE;
	screen->authPasswdData = NULL;
	screen->authPasswdFirstViewOnly = 1;

	screen->width = width;
	screen->height = height;
	screen->bitsPerPixel = screen->depth = 8*bytesPerPixel;

	screen->passwordCheck = rfbDefaultPasswordCheck;

	screen->ignoreSIGPIPE = TRUE;

	/* disable progressive updating per default */
	screen->progressiveSliceHeight = 0;

	screen->listenInterface = htonl(INADDR_ANY);

	screen->deferUpdateTime=5;
	screen->maxRectsPerUpdate=50;

	screen->handleEventsEagerly = FALSE;

	screen->protocolMajorVersion = rfbProtocolMajorVersion;
	screen->protocolMinorVersion = rfbProtocolMinorVersion;

	screen->permitFileTransfer = FALSE;

	if(!rfbProcessArguments(screen,argc,argv)) {
		free(screen);
		return NULL;
	}

#ifdef WIN32
{
		DWORD dummy=255;
		GetComputerName(screen->thisHost,&dummy);
}
#else
gethostname(screen->thisHost, 255);
#endif

screen->paddedWidthInBytes = width*bytesPerPixel;

/* format */

rfbInitServerFormat(screen, bitsPerSample);

/* cursor */

screen->cursorX=screen->cursorY=screen->underCursorBufferLen=0;
screen->underCursorBuffer=NULL;
screen->dontConvertRichCursorToXCursor = FALSE;
screen->cursor = &myCursor;
INIT_MUTEX(screen->cursorMutex);

IF_PTHREADS(screen->backgroundLoop = FALSE);

/* proc's and hook's */

screen->kbdAddEvent = rfbDefaultKbdAddEvent;
screen->kbdReleaseAllKeys = rfbDoNothingWithClient;
screen->ptrAddEvent = rfbDefaultPtrAddEvent;
screen->setXCutText = rfbDefaultSetXCutText;
screen->getCursorPtr = rfbDefaultGetCursorPtr;
screen->setTranslateFunction = rfbSetTranslateFunction;
screen->newClientHook = rfbDefaultNewClientHook;
screen->displayHook = NULL;
screen->getKeyboardLedStateHook = NULL;

/* initialize client list and iterator mutex */
rfbClientListInit(screen);

return(screen);
}

/*
 * Switch to another framebuffer (maybe of different size and color
 * format). Clients supporting NewFBSize pseudo-encoding will change
 * their local framebuffer dimensions if necessary.
 * NOTE: Rich cursor data should be converted to new pixel format by
 * the caller.
 */

void rfbNewFramebuffer(rfbScreenInfoPtr screen, char *framebuffer,
		int width, int height,
		int bitsPerSample, int samplesPerPixel,
		int bytesPerPixel)
{
	rfbPixelFormat old_format;
	rfbBool format_changed = FALSE;
	rfbClientIteratorPtr iterator;
	rfbClientPtr cl;

	/* Update information in the screenInfo structure */

	old_format = screen->serverFormat;

	if (width & 3)
		rfbErr("WARNING: New width (%d) is not a multiple of 4.\n", width);

	screen->width = width;
	screen->height = height;
	screen->bitsPerPixel = screen->depth = 8*bytesPerPixel;
	screen->paddedWidthInBytes = width*bytesPerPixel;

	rfbInitServerFormat(screen, bitsPerSample);

	if (memcmp(&screen->serverFormat, &old_format,
			sizeof(rfbPixelFormat)) != 0) {
		format_changed = TRUE;
	}

	screen->frameBuffer = framebuffer;

	/* Adjust pointer position if necessary */

	if (screen->cursorX >= width)
		screen->cursorX = width - 1;
	if (screen->cursorY >= height)
		screen->cursorY = height - 1;

	/* For each client: */
	iterator = rfbGetClientIterator(screen);
	while ((cl = rfbClientIteratorNext(iterator)) != NULL) {

		/* Re-install color translation tables if necessary */

		if (format_changed)
			screen->setTranslateFunction(cl);

		/* Mark the screen contents as changed, and schedule sending
       NewFBSize message if supported by this client. */

		LOCK(cl->updateMutex);
		sraRgnDestroy(cl->modifiedRegion);
		cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height);
		sraRgnMakeEmpty(cl->copyRegion);
		cl->copyDX = 0;
		cl->copyDY = 0;

		if (cl->useNewFBSize)
			cl->newFBSizePending = TRUE;

		TSIGNAL(cl->updateCond);
		UNLOCK(cl->updateMutex);
	}
	rfbReleaseClientIterator(iterator);
}

/* hang up on all clients and free all reserved memory */

void rfbScreenCleanup(rfbScreenInfoPtr screen)
{
	rfbClientIteratorPtr i=rfbGetClientIterator(screen);
	rfbClientPtr cl,cl1=rfbClientIteratorNext(i);
	while(cl1) {
		cl=rfbClientIteratorNext(i);
		rfbClientConnectionGone(cl1);
		cl1=cl;
	}
	rfbReleaseClientIterator(i);

#define FREE_IF(x) if(screen->x) free(screen->x)
	FREE_IF(colourMap.data.bytes);
	FREE_IF(underCursorBuffer);
	TINI_MUTEX(screen->cursorMutex);
	if(screen->cursor && screen->cursor->cleanup)
		rfbFreeCursor(screen->cursor);

	rfbRRECleanup(screen);
	rfbCoRRECleanup(screen);
	rfbUltraCleanup(screen);
#ifdef LIBVNCSERVER_HAVE_LIBZ
	rfbZlibCleanup(screen);
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
	rfbTightCleanup(screen);
#endif

	/* free all 'scaled' versions of this screen */
	while (screen->scaledScreenNext!=NULL)
	{
		rfbScreenInfoPtr ptr;
		ptr = screen->scaledScreenNext;
		screen->scaledScreenNext = ptr->scaledScreenNext;
		free(ptr->frameBuffer);
		free(ptr);
	}

#endif
	free(screen);
}

void rfbInitServer(rfbScreenInfoPtr screen)
{
#ifdef WIN32
	WSADATA trash;
	WSAStartup(MAKEWORD(2,2),&trash);
#endif
	rfbInitSockets(screen);
	rfbHttpInitSockets(screen);
#ifndef __MINGW32__
	if(screen->ignoreSIGPIPE)
		signal(SIGPIPE,SIG_IGN);
#endif
}

void rfbShutdownServer(rfbScreenInfoPtr screen,rfbBool disconnectClients) {
	if(disconnectClients) {
		rfbClientPtr cl;
		rfbClientIteratorPtr iter = rfbGetClientIterator(screen);
		while( (cl = rfbClientIteratorNext(iter)) )
			if (cl->sock > -1)
				/* we don't care about maxfd here, because the server goes away */
				rfbCloseClient(cl);
		rfbReleaseClientIterator(iter);
	}

	rfbShutdownSockets(screen);
	rfbHttpShutdownSockets(screen);
}

#ifndef LIBVNCSERVER_HAVE_GETTIMEOFDAY
#include <fcntl.h>
#include <conio.h>
#include <sys/timeb.h>

void gettimeofday(struct timeval* tv,char* dummy)
{
	SYSTEMTIME t;
	GetSystemTime(&t);
	tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond;
	tv->tv_usec=t.wMilliseconds*1000;
}
#endif

rfbBool
rfbProcessEvents(rfbScreenInfoPtr screen,long usec)
{
	rfbClientIteratorPtr i;
	rfbClientPtr cl,clPrev;
	struct timeval tv;
	rfbBool result=FALSE;
	extern rfbClientIteratorPtr
	rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen);

	if(usec<0)
		usec=screen->deferUpdateTime*1000;

	rfbCheckFds(screen,usec);
	rfbHttpCheckFds(screen);
#ifdef CORBA
	corbaCheckFds(screen);
#endif

	i = rfbGetClientIteratorWithClosed(screen);
	cl = rfbClientIteratorHead(i);
	while(cl) {
		if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) &&
				!sraRgnEmpty(cl->requestedRegion)) {
			result=TRUE;
			if(screen->deferUpdateTime == 0) {
				rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
			} else if(cl->startDeferring.tv_usec == 0) {
				gettimeofday(&cl->startDeferring, NULL);
				if(cl->startDeferring.tv_usec == 0)
					cl->startDeferring.tv_usec++;
			} else {
				gettimeofday(&tv,NULL);
				if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */
						|| ((tv.tv_sec-cl->startDeferring.tv_sec)*1000
								+(tv.tv_usec-cl->startDeferring.tv_usec)/1000)
								> screen->deferUpdateTime) {
					cl->startDeferring.tv_usec = 0;
					rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
				}
			}
		}

		if (!cl->viewOnly && cl->lastPtrX >= 0) {
			if(cl->startPtrDeferring.tv_usec == 0) {
				gettimeofday(&cl->startPtrDeferring,NULL);
				if(cl->startPtrDeferring.tv_usec == 0)
					cl->startPtrDeferring.tv_usec++;
			} else {
				struct timeval tv;
				gettimeofday(&tv,NULL);
				if(tv.tv_sec < cl->startPtrDeferring.tv_sec /* at midnight */
						|| ((tv.tv_sec-cl->startPtrDeferring.tv_sec)*1000
								+(tv.tv_usec-cl->startPtrDeferring.tv_usec)/1000)
								> cl->screen->deferPtrUpdateTime) {
					cl->startPtrDeferring.tv_usec = 0;
					cl->screen->ptrAddEvent(cl->lastPtrButtons,
							cl->lastPtrX,
							cl->lastPtrY, cl);
					cl->lastPtrX = -1;
				}
			}
		}
		clPrev=cl;
		cl=rfbClientIteratorNext(i);
		if(clPrev->sock==-1) {
			rfbClientConnectionGone(clPrev);
			result=TRUE;
		}
	}
	rfbReleaseClientIterator(i);

	return result;
}

rfbBool rfbIsActive(rfbScreenInfoPtr screenInfo) {
	return screenInfo->socketState!=RFB_SOCKET_SHUTDOWN || screenInfo->clientHead!=NULL;
}

void rfbRunEventLoop(rfbScreenInfoPtr screen, long usec, rfbBool runInBackground)
{
	if(runInBackground) {
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
		pthread_t listener_thread;

		screen->backgroundLoop = TRUE;

		pthread_create(&listener_thread, NULL, listenerRun, screen);
		return;
#else
		rfbErr("Can't run in background, because I don't have PThreads!\n");
		return;
#endif
	}

	if(usec<0)
		usec=screen->deferUpdateTime*1000;

	while(rfbIsActive(screen))
		rfbProcessEvents(screen,usec);
}
